/*
 * filteredPicture.cpp
 *
 *  Created on: Aug 25, 2011
 *  Created by: Kevin Yang
 */

#include "filteredPicture.h"
#include <iostream>
using namespace std;
///////////////////////////////////////////////////////////////////////////
// This constructor allocates memory for paddedPixels and filteredPixels.
///////////////////////////////////////////////////////////////////////////
filteredPicture::filteredPicture() {
	// paddedPixels[height+1][width+1]
	paddedPixels = new int*[height+2];
	for (int i = 0 ; i< height+2 ; i++){
		paddedPixels[i] = new int[width+2];
	}

	// filteredPixels[height][width]
	filteredPixels = new int*[height];
	for (int i = 0 ; i< height ; i++){
		filteredPixels[i] = new int[width];
	}
}

//////////////////////////////////////////////////////////////////////////
// Copy Constructor
/////////////////////////////////////////////////////////////////////////
filteredPicture::filteredPicture(const filteredPicture &source): twoDimensionalPicture(source){
	for (int i = 0; i < height+2; i++){
		for (int j =0; j < width+2; j++){
			paddedPixels[i][j] = source.paddedPixels[i][j];
		}
	}
	for (int i = 0; i < height; i++){
		for (int j = 0; j < width; j++){
			filteredPixels[i][j] = source.filteredPixels[i][j];
		}
	}
}

/////////////////////////////////////////////////////////////////////////////
// Overloaded Assignment
/////////////////////////////////////////////////////////////////////////////
filteredPicture& filteredPicture::operator= (const filteredPicture &source){
	if (this == &source)
		return *this;
	twoDimensionalPicture::operator =(source);
	for (int i = 0; i < height+2; i++){
		for (int j =0; j < width+2; j++){
			paddedPixels[i][j] = source.paddedPixels[i][j];
		}
	}
	for (int i = 0; i < height; i++){
		for (int j = 0; j < width; j++){
			filteredPixels[i][j] = source.filteredPixels[i][j];
		}
	}
	return *this;
}

filteredPicture::~filteredPicture() {
	delete [] paddedPixels;
	delete [] filteredPixels;
}

////////////////////////////////////////////////////////////////////////
// This method processes the pixel picture from the parent class and
// generates the median filtered picture
//
// step 1: generate the padded picture
// step 2: run the median filter algorithm.
////////////////////////////////////////////////////////////////////////
void filteredPicture::medianFilterPicture(){
	// generate the padded picture by copying the outer row and columns.
	// first copy the original picture
	int i;
	int j;
	for (i = 1; i< height+1 ; i++){
		for (j = 1; j < width+1; j++){
			paddedPixels[i][j] = pixels[i-1][j-1];
		}
	}
	// next copy the rows and columns
	i = 0;
	for(j = 1; j < width+1; j++){
		paddedPixels[i][j] = pixels[i][j-1];
	}
	i = height+1;
	for(j = 1; j < width+1; j++){
		paddedPixels[i][j] = pixels[i-2][j-1];
	}
	j = 0;
	for(i = 1; i < height+1; i++){
		paddedPixels[i][j] = pixels[i-1][j];
	}
	j = width+1;
	for(i = 1; i < height+1; i++){
		paddedPixels[i][j] = pixels[i-1][j-2];
	}
	//fill the corners
	paddedPixels[0][0] = pixels[0][0];
	paddedPixels[0][width+1] = pixels[0][width-1];
	paddedPixels[height+1][0] = pixels[height-1][0];
	paddedPixels[height+1][width+1] = pixels[height-1][width-1];

	// Filter the pixels, 9 pixels surronding the current pixel.
	// Allocate the 3x3 matrix in memory.
	int ninePixels[3][3];
	int ninePixelsRowSorted[3][3];

	for (int i = 1; i < height +1; i++){
		for (int j = 1; j < width+1; j++){
			ninePixels[0][0] = paddedPixels[i-1][j-1];
			ninePixels[0][1] = paddedPixels[i-1][j];
			ninePixels[0][2] = paddedPixels[i-1][j+1];

			ninePixels[1][0] = paddedPixels[i][j-1];
			ninePixels[1][1] = paddedPixels[i][j];
			ninePixels[1][2] = paddedPixels[i][j+1];

			ninePixels[2][0] = paddedPixels[i+1][j-1];
			ninePixels[2][1] = paddedPixels[i+1][j];
			ninePixels[2][2] = paddedPixels[i+1][j+1];

			// first run of median algo: find min, med, max of all 3 rows.
			// This algorithm will make 17 total comparisons to get the median
			for (int row = 0; row < 3; row++){
				if (ninePixels[row][0]> ninePixels[row][1]){ // 0> 1
					if (ninePixels[row][0] > ninePixels[row][2]){ // 0 > 2
						ninePixelsRowSorted[row][2] = ninePixels[row][0]; // max
						if (ninePixels[row][1]>ninePixels[row][2]){ // 0 > 1 > 2
							ninePixelsRowSorted[row][1] = ninePixels[row][1];
							ninePixelsRowSorted[row][0] = ninePixels[row][2];
						}
						else{ // 0 > 2 > 1
							ninePixelsRowSorted[row][1] = ninePixels[row][2];
							ninePixelsRowSorted[row][0] = ninePixels[row][1];
						}
					}
					else { // (ninePixels[row][0]> ninePixels[row][1]) && !(ninePixels[row][0]> ninePixels[row][1])
						// so [1]<=[0]<=[2]
						ninePixelsRowSorted[row][2] = ninePixels[row][2];
						ninePixelsRowSorted[row][1] = ninePixels[row][0];
						ninePixelsRowSorted[row][0] = ninePixels[row][1];
					}
				}

				else { //[0]<=[1]
					if (ninePixels[row][1] > ninePixels[row][2]){
						//[0] <= [1], [2] <= 1, so max = [2]
						ninePixelsRowSorted[row][2] = ninePixels[row][1];
						if (ninePixels[row][0] > ninePixels[row][2]){
							// [2]<=[0]<=[1]
							ninePixelsRowSorted[row][1] = ninePixels[row][0];
							ninePixelsRowSorted[row][0] = ninePixels[row][2];
						}
						else {
							// [0]<=[2]<=[1]
							ninePixelsRowSorted[row][1] = ninePixels[row][2];
							ninePixelsRowSorted[row][0] = ninePixels[row][0];
						}
					}
					else { //[0]<=[1]<=[2]
						ninePixelsRowSorted[row][2] = ninePixels[row][2];
						ninePixelsRowSorted[row][1] = ninePixels[row][1];
						ninePixelsRowSorted[row][0] = ninePixels[row][0];
					}
				}
			} // finished filling row sorted matrix.

			// Now ninePixelsRowSorted[][] has 3 groups:
			// ninePixelsRowsSorted[][2] contains the 3 maximums
			// ninePixelsRowsSorted[][1] contains the 3 medians
			// ninePixelsRowsSorted[][0] contains the 3 minimums

			// now find the minimum of the 3 maximums
			// the median of the 3 medians
			// the maximum of the 3 minimums
			int minMax, medMed, maxMin;
			// find minMax
			minMax = findMinMedMax(ninePixelsRowSorted[0][2], ninePixelsRowSorted[1][2],ninePixelsRowSorted[2][2], 1);
			medMed = findMinMedMax(ninePixelsRowSorted[0][1], ninePixelsRowSorted[1][1],ninePixelsRowSorted[2][1], 2);
			maxMin = findMinMedMax(ninePixelsRowSorted[0][0], ninePixelsRowSorted[1][0],ninePixelsRowSorted[2][0], 3);

			// Finally the median of the 9 pixels is
			// the median of minMax, medMed, and maxMin
			filteredPixels[i-1][j-1] = findMinMedMax(minMax, medMed, maxMin, 2);
		}
		// garbage collection
	}
	delete[] ninePixels;
	return;

}

/////////////////////////////////////////////////////////////////////////////////
// This method returns the min, med, or max of 3 input integers, depending on the
// last variable minMedMax:
// minMedMax = 1: min
// minMedMax = 2: med
// minMedMax = 3: max
/////////////////////////////////////////////////////////////////////////////////
int filteredPicture::findMinMedMax(int In0, int In1, int In2, int minMedMax){
	if (minMedMax<1 || minMedMax>3){
		cout << "Error: not sure what this is calculating";
		return -1;
	}
	if(In0 > In1){ // [1] <= [0]
		if (In0 > In2) { // [1] <= [0], [2] <= [0]
			if (In1> In2) {
				//[2] <= [1] <= [0]
				if (minMedMax == 1)	// min
					return In2;
				else if (minMedMax == 2)	//med
					return In1;
				else //max
					return In0;
			}
			else{ // [1] <= [2] <= [0]
				if (minMedMax == 1)	// min
					return In1;
				else if (minMedMax == 2)	//med
					return In2;
				else //max
					return In0;
			}
		}
		else{ // [1]<=[0]<=[2]
			if (minMedMax == 1)	// min
				return In1;
			else if (minMedMax == 2)	//med
				return In0;
			else //max
				return In2;
		}
	}
	else { //[0] <= [1]
		if (In1 > In2){ // [0] <= [1], [2] <= [1]
			if (In2> In0){ // 0 <= 2 <= 1
				if (minMedMax == 1)	// min
					return In0;
				else if (minMedMax == 2)	//med
					return In2;
				else //max
					return In1;
			}
			else{ // 2 <= 0 <= 1
				if (minMedMax == 1)	// min
					return In2;
				else if (minMedMax == 2)	//med
					return In0;
				else //max
					return In1;
			}
		}
		else{ // 0 <= 1 <= 2
			if (minMedMax == 1)	// min
				return In0;
			else if (minMedMax == 2)	//med
				return In1;
			else //max
				return In2;
		}
	}
	cout << "Error, should not have gotten here" << endl;
	return -1;
}

//////////////////////////////////////////////////////////////////
// This method will print out the integer value of the paddedPixels
//////////////////////////////////////////////////////////////////

void filteredPicture::printPaddedPixels(){
	for(int i = 0; i < height+2; i++){
		for (int j = 0; j < width+2; j++){
			cout << paddedPixels[i][j] << "\t";
		}
		cout << endl;
	}
}

//////////////////////////////////////////////////////////////////
// This method will print out the integer value of the filteredPixels
//////////////////////////////////////////////////////////////////

void filteredPicture::printFilteredPixels(){
	for(int i = 0; i < height ; i++){
		for (int j = 0; j < width; j++){
			cout << filteredPixels[i][j] << "\t";
		}
		cout << endl;
	}
}
